package main

// https://github.com/hezhizheng/go-reverse-proxy
import (
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
)

var hostProxy = map[string]*httputil.ReverseProxy{}

type baseHandle struct {
	proxyMap map[string]proxyResult
}

func (h *baseHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	host := r.Host

	// cache
	if fn, ok := hostProxy[host]; ok {
		fn.ServeHTTP(w, r)
		return
	}

	if target, ok := h.proxyMap[host]; ok {

		remoteUrl := target.ReverseUrl

		proxy := httputil.NewSingleHostReverseProxy(remoteUrl)
		// cache
		hostProxy[host] = proxy

		targetQuery := remoteUrl.RawQuery
		r.URL.Scheme = remoteUrl.Scheme
		r.URL.Host = remoteUrl.Host
		r.Host = remoteUrl.Host
		r.URL.Path, r.URL.RawPath = joinURLPath(target.ReverseUrl, r.URL)

		if targetQuery == "" || r.URL.RawQuery == "" {
			r.URL.RawQuery = targetQuery + r.URL.RawQuery
		} else {
			r.URL.RawQuery = targetQuery + "&" + r.URL.RawQuery
		}
		if _, ok := r.Header["User-Agent"]; !ok {
			// explicitly disable User-Agent so it's not set to default value
			r.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36")
			return
		}
		log.Println("request.URL.Path：", r.URL.Path, "request.URL.RawQuery：", r.URL.RawQuery)

		proxy.ServeHTTP(w, r)

		// 修改响应头
		proxy.ModifyResponse = func(response *http.Response) error {
			response.Header.Add("Access-Control-Allow-Origin", "*")
			//response.Header.Add("Reverse-Proxy-Server-PowerBy", "http://xx.com")
			return nil
		}

		return
	}
	w.Write([]byte("403: Host forbidden " + host))
}

//
//func NewSingleHostReverseProxy(proxyMap map[string]proxyResult) httputil.ReverseProxy {
//	target, _ := proxyMap["test.com"]
//
//	director := func(request *http.Request) {
//
//		//if _, ok := req.Header["User-Agent"]; !ok && req.URL.Scheme == "http" {
//		//	// explicitly disabled User-Agent so redirect to https
//		//	newReq, _ := http.NewRequest("GET", req.URL.String(), nil)
//		//	newReq.Header.Set("Connection", "Upgrade")
//		//	newReq.Header.Set("Upgrade", "http/1.1")
//		//	proxy := httputil.NewSingleHostReverseProxy(target.ResolveReference(&url.URL{Scheme: "https"}))
//		//	proxy.ServeHTTP(req.Response, newReq)
//		//	return
//		//}
//		//if target.RawQuery != "" {
//		//	req.URL.RawQuery = target.RawQuery
//		//}
//		//if _, ok := req.Header["User-Agent"]; !ok {
//		//	// explicitly disabled User-Agent
//		//	req.Header.Set("User-Agent", "")
//		//}
//		//log.Println("request.URL.Path：", req.URL.Path, "request.URL.RawQuery：", req.URL.RawQuery)
//
//		targetQuery := target.ReverseUrl.RawQuery
//		request.URL.Scheme = target.ReverseUrl.Scheme
//		request.URL.Host = target.ReverseUrl.Host
//		request.Host = target.ReverseUrl.Host // todo 这个是关键
//		request.URL.Path, request.URL.RawPath = joinURLPath(target.ReverseUrl, request.URL)
//
//		if targetQuery == "" || request.URL.RawQuery == "" {
//			request.URL.RawQuery = targetQuery + request.URL.RawQuery
//		} else {
//			request.URL.RawQuery = targetQuery + "&" + request.URL.RawQuery
//		}
//		if _, ok := request.Header["User-Agent"]; !ok {
//			// explicitly disable User-Agent so it's not set to default value
//			request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36")
//			return
//		}
//		log.Println("request.URL.Path：", request.URL.Path, "request.URL.RawQuery：", request.URL.RawQuery)
//	}
//
//	return httputil.ReverseProxy{Director: director}
//	//return proxy
//}
//func GoReverseProxy(proxyMap map[string]proxyResult) httputil.ReverseProxy {
//
//	proxy := NewSingleHostReverseProxy(proxyMap)
//
//	// 修改响应头
//	proxy.ModifyResponse = func(response *http.Response) error {
//		response.Header.Add("Access-Control-Allow-Origin", "*")
//		//response.Header.Add("Reverse-Proxy-Server-PowerBy", "http://xx.com")
//		return nil
//	}
//
//	return proxy
//}

// go sdk 源码
func joinURLPath(a, b *url.URL) (path, rawpath string) {
	if a.RawPath == "" && b.RawPath == "" {
		return singleJoiningSlash(a.Path, b.Path), ""
	}
	// Same as singleJoiningSlash, but uses EscapedPath to determine
	// whether a slash should be added
	apath := a.EscapedPath()
	bpath := b.EscapedPath()

	aslash := strings.HasSuffix(apath, "/")
	bslash := strings.HasPrefix(bpath, "/")

	switch {
	case aslash && bslash:
		return a.Path + b.Path[1:], apath + bpath[1:]
	case !aslash && !bslash:
		return a.Path + "/" + b.Path, apath + "/" + bpath
	}
	return a.Path + b.Path, apath + bpath
}

// go sdk 源码
func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}
